//
// Copyright 2019 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
// Package clipping provides utilities for deciding which events in eventpb.EventSets are clipped.
package clipping

import (
	"errors"

	eventpb "github.com/google/schedviz/tracedata/schedviz_events_go_proto"
)

func copyMap(overflowed map[int64]struct{}) map[int64]struct{} {
	attestedCPUs := make(map[int64]struct{}, len(overflowed))
	for key, value := range overflowed {
		attestedCPUs[key] = value
	}
	return attestedCPUs
}

// ClipFromStartOfTrace marks clipped events. Given an input EventSet and a set of overflowedCPUs,
// it determines the latest first timestamp among events generated by the overflowedCPUs, and marks
// all events prior to that time on these CPUs as Clipped. Events are expected to be sorted by timestamp.
func ClipFromStartOfTrace(es *eventpb.EventSet, overflowedCPUs map[int64]struct{}) error {
	if len(overflowedCPUs) == 0 {
		return nil
	}
	attested := copyMap(overflowedCPUs)
	for _, ev := range es.Event {
		if _, ok := attested[ev.GetCpu()]; ok {
			// First time we've seen an event from this CPU.  Remove it from soughtCPUs.
			delete(attested, ev.GetCpu())
		}
		if len(attested) == 0 {
			return nil
		}
		ev.Clipped = true
	}
	return errors.New("Not all overflowed CPUs emitted events")
}

// ClipFromEndOfTrace marks clipped events. Given an input EventSet and a set of overflowedCPUs,
// it determines the earliest last timestamp among events generated by the overflowedCPUs, and marks
// all events prior to that time on these CPUs as Clipped. Events are expected to be sorted by timestamp.
func ClipFromEndOfTrace(es *eventpb.EventSet, overflowedCPUs map[int64]struct{}) error {
	if len(overflowedCPUs) == 0 {
		return nil
	}
	attested := copyMap(overflowedCPUs)
	for idx := len(es.Event) - 1; idx >= 0; idx-- {
		ev := es.Event[idx]
		if _, ok := attested[ev.GetCpu()]; ok {
			// First time we've seen an event from this CPU.  Remove it from soughtCPUs.
			delete(attested, ev.GetCpu())
		}
		if len(attested) == 0 {
			return nil
		}
		ev.Clipped = true
	}
	return errors.New("Not all overflowed CPUs emitted events")
}
